
`include "defines.v"
//----------------------------------------------------------------
//Module Name : axi_rw.v
//Description of module:
// use axi4 to read or write data from ram/rom
//----------------------------------------------------------------
//Designer:	Tang Pengyu
//Date: 2021/09/08  
//----------------------------------------------------------------

// Burst types
`define AXI_BURST_TYPE_FIXED                                2'b00
`define AXI_BURST_TYPE_INCR                                 2'b01
`define AXI_BURST_TYPE_WRAP                                 2'b10
// Access permissions  for prot
`define AXI_PROT_UNPRIVILEGED_ACCESS                        3'b000
`define AXI_PROT_PRIVILEGED_ACCESS                          3'b001
`define AXI_PROT_SECURE_ACCESS                              3'b000
`define AXI_PROT_NON_SECURE_ACCESS                          3'b010
`define AXI_PROT_DATA_ACCESS                                3'b000
`define AXI_PROT_INSTRUCTION_ACCESS                         3'b100
// Memory types (AR)
`define AXI_ARCACHE_DEVICE_NON_BUFFERABLE                   4'b0000
`define AXI_ARCACHE_DEVICE_BUFFERABLE                       4'b0001
`define AXI_ARCACHE_NORMAL_NON_CACHEABLE_NON_BUFFERABLE     4'b0010
`define AXI_ARCACHE_NORMAL_NON_CACHEABLE_BUFFERABLE         4'b0011
`define AXI_ARCACHE_WRITE_THROUGH_NO_ALLOCATE               4'b1010
`define AXI_ARCACHE_WRITE_THROUGH_READ_ALLOCATE             4'b1110
`define AXI_ARCACHE_WRITE_THROUGH_WRITE_ALLOCATE            4'b1010
`define AXI_ARCACHE_WRITE_THROUGH_READ_AND_WRITE_ALLOCATE   4'b1110
`define AXI_ARCACHE_WRITE_BACK_NO_ALLOCATE                  4'b1011
`define AXI_ARCACHE_WRITE_BACK_READ_ALLOCATE                4'b1111
`define AXI_ARCACHE_WRITE_BACK_WRITE_ALLOCATE               4'b1011
`define AXI_ARCACHE_WRITE_BACK_READ_AND_WRITE_ALLOCATE      4'b1111
// Memory types (AW)
`define AXI_AWCACHE_DEVICE_NON_BUFFERABLE                   4'b0000
`define AXI_AWCACHE_DEVICE_BUFFERABLE                       4'b0001
`define AXI_AWCACHE_NORMAL_NON_CACHEABLE_NON_BUFFERABLE     4'b0010
`define AXI_AWCACHE_NORMAL_NON_CACHEABLE_BUFFERABLE         4'b0011
`define AXI_AWCACHE_WRITE_THROUGH_NO_ALLOCATE               4'b0110
`define AXI_AWCACHE_WRITE_THROUGH_READ_ALLOCATE             4'b0110
`define AXI_AWCACHE_WRITE_THROUGH_WRITE_ALLOCATE            4'b1110
`define AXI_AWCACHE_WRITE_THROUGH_READ_AND_WRITE_ALLOCATE   4'b1110
`define AXI_AWCACHE_WRITE_BACK_NO_ALLOCATE                  4'b0111
`define AXI_AWCACHE_WRITE_BACK_READ_ALLOCATE                4'b0111
`define AXI_AWCACHE_WRITE_BACK_WRITE_ALLOCATE               4'b1111
`define AXI_AWCACHE_WRITE_BACK_READ_AND_WRITE_ALLOCATE      4'b1111

`define AXI_SIZE_BYTES_1                                    3'b000
`define AXI_SIZE_BYTES_2                                    3'b001
`define AXI_SIZE_BYTES_4                                    3'b010
`define AXI_SIZE_BYTES_8                                    3'b011
`define AXI_SIZE_BYTES_16                                   3'b100
`define AXI_SIZE_BYTES_32                                   3'b101
`define AXI_SIZE_BYTES_64                                   3'b110
`define AXI_SIZE_BYTES_128                                  3'b111


module	axi_rw # (
		parameter	RW_DATA_WIDTH = 64,
		parameter	RW_ADDR_WIDTH = 64,
		parameter	AXI_DATA_WIDTH = 64,
		parameter	AXI_ADDR_WIDTH = 64,
		parameter	AXI_ID_WIDTH = 4,
		parameter	AXI_USER_WIDTH = 1
)(
		//glabal
		input	clock,
		input	reset,					//high work
		
		//user input/output
		input	rw_valid_i,
		output	rw_ready_o,
		input	rw_req_i,				//require read/write
		output	reg	[RW_DATA_WIDTH-1:0] data_read_o,
		input	[RW_DATA_WIDTH-1:0] data_write_i,
		input	[RW_ADDR_WIDTH-1:0] rw_addr_i,
		input	[1:0]	rw_size_i,			//传输的字节数
		output	[1:0]	rw_resp_o,
		
		//MASTER write addr
		input	aw_ready_i,			//slave -> master,ready to receive write address
		output	aw_valid_o,			//master -> slave,write address valid
		output	[AXI_ADDR_WIDTH-1:0] aw_addr_o,		//write sddress
		output	[AXI_ID_WIDTH-1:0]	aw_id_o,			//write address channel ID
		output	[AXI_USER_WIDTH-1:0] aw_user_o,		//自定义
		output	[2:0]	aw_prot_o,				//access permissions
		output	[7:0]	aw_len_o,			//burst lenth = aw_len + 1
		output	[2:0]	aw_size_o,			//本次burst中，一次transferde的字节数
		output	[1:0]	aw_burst_o,			//burst_type
		output			aw_lock_o,
		output	[3:0]	aw_cache_o,			//memory types
		output	[3:0]	aw_qos_o,			//Quality of service identifier for a write transaction
		output	[3:0]	aw_region_o,		//多接口时用
		
		//master write data
		input	w_ready_i,
		output	w_valid_o,
		output	[AXI_DATA_WIDTH-1:0]	w_data_o,
		output	[7:0]	w_strb_o,				//标志有效位
		output	w_last_o,						//标志最后一次传输
		output	[AXI_USER_WIDTH-1:0]	w_user_o,
		
		//write response
		output	b_ready_o,
		input	b_valid_i,
		input	[1:0]	b_resp_i,
		input	[AXI_ID_WIDTH-1:0]	b_id_i,
		input	[AXI_USER_WIDTH-1:0]	b_user_i,
		
		//read address channel
		input	ar_ready_i,
		output	ar_valid_o,
		output	[AXI_ADDR_WIDTH-1:0]	ar_addr_o,
		output	[2:0]	ar_prot_o,
		output	[AXI_ID_WIDTH-1:0]	ar_id_o,			//read address channel identifier
		output	[AXI_USER_WIDTH-1:0]	ar_user_o,
		output	[7:0]	ar_len_o,
		output	[2:0]	ar_size_o,
		output	[1:0]	ar_burst_o,
		output			ar_lock_o,
		output	[3:0]	ar_cache_o,
		output	[3:0]	ar_qos_o,
		output	[3:0]	ar_region_o,
		
		//read data channel
		output	r_ready_o,
		input	r_valid_i,
		input	[1:0]	r_resp_i,
		input	[AXI_DATA_WIDTH-1:0]	r_data_i,
		input	r_last_i,
		input	[AXI_ID_WIDTH-1:0]	r_id_i,
		input	[AXI_USER_WIDTH-1:0]	r_user_i	
		
	);
	
	wire	w_trans;
	wire	r_trans;
	assign	w_trans = (rw_req_i == `REQ_WRITE);
	assign	r_trans = (rw_req_i == `REQ_READ);
//write or read en 
	wire	w_valid;
	wire	r_valid;
	assign	w_valid = rw_valid_i & w_trans;
	assign	r_valid = rw_valid_i & r_trans;
//handshake signal
	wire	aw_hs = aw_valid_o & aw_ready_i;
	wire	w_hs = w_valid_o & w_ready_i;
	wire	b_hs = b_valid_i & b_ready_o;
	
	wire	ar_hs = ar_valid_o & ar_ready_i;
	wire	r_hs = r_valid_i & r_ready_o;
	
	wire	w_done = w_hs & w_last_o;				//写如最后一次transfer
	wire	r_done = r_hs & r_last_i;
	wire	trans_done = w_trans ? b_hs : r_done;
	

//write state machine
	parameter	[1:0]	W_STATE_IDLE = 2'b00,
						W_STATE_ADDR = 2'b01,
						W_STATE_WRITE = 2'b10,
						W_STATE_RESP = 2'b11;
	reg	[1:0]	w_state;
	reg	[1:0]	w_state_next;
	always @(posedge clock)
	  begin
		if(reset)
			w_state <= W_STATE_IDLE;
		else
			w_state <= (w_valid & rw_ready == 1'b0) ? w_state_next : w_state;
	  end
	  
	always @(*)
	  begin
		case(w_state)
			W_STATE_IDLE:	w_state_next = W_STATE_ADDR;
			W_STATE_ADDR:	w_state_next = aw_hs ? W_STATE_WRITE : W_STATE_ADDR;
			W_STATE_WRITE:	w_state_next = w_done ? W_STATE_RESP : W_STATE_WRITE;
			W_STATE_RESP:	w_state_next = b_hs ? W_STATE_IDLE : W_STATE_RESP;
			default:		w_state_next = W_STATE_IDLE;
		endcase
	  end
	  

//read state machine
	parameter	[1:0]	R_STATE_IDLE = 2'b00,
						R_STATE_ADDR = 2'b01,
						R_STATE_READ = 2'b10;
	reg	[1:0]	r_state;
	reg	[1:0]	r_state_next;
	
	always @(posedge clock)
	  begin
		if(reset)
			r_state <= R_STATE_IDLE;
		else
			r_state <= (r_valid & rw_ready == 1'b0) ? r_state_next : r_state;	  
	  end
	always @(*)
	  begin
		case(r_state)
			R_STATE_IDLE:	r_state_next = R_STATE_ADDR;
			R_STATE_ADDR:	r_state_next = ar_hs ? R_STATE_READ : R_STATE_ADDR;
			R_STATE_READ:	r_state_next = r_done ? R_STATE_IDLE : R_STATE_READ;
			default:		r_state_next = R_STATE_IDLE;
		endcase
	  end


//process data
	parameter	ALIGNED_WIDTH = $clog2(AXI_DATA_WIDTH / 8);			//对其需要看的地址位宽
	parameter	OFFSET_WIDTH = $clog2(AXI_DATA_WIDTH);
	parameter	AXI_SIZE = $clog2(AXI_DATA_WIDTH / 8);
	parameter	MASK_WIDTH = AXI_DATA_WIDTH * 2;
	parameter	TRANS_LEN = RW_DATA_WIDTH / AXI_DATA_WIDTH;
	parameter	BLOCK_TRANS = TRANS_LEN > 1 ? 1'b1 : 1'b0;
	
	
	wire	aligned;				//对齐传输1'b1
	assign	aligned = BLOCK_TRANS | (rw_addr_i[ALIGNED_WIDTH-1:0] == 0);			//成块传输或起始地址对齐
	wire	size_b;
	wire	size_h;
	wire	size_w;
	wire	size_d;
	assign	size_b = (rw_size_i == `SIZE_B);
	assign	size_h = (rw_size_i == `SIZE_H);
	assign	size_w = (rw_size_i == `SIZE_W);
	assign	size_d = (rw_size_i == `SIZE_D);
	wire	[3:0]	addr_op1;				//取起始后四位地址
	wire	[3:0]	addr_op2;				//地址增量
	wire	[3:0]	addr_end;				//结束地址后四位
	assign	addr_op1 = {{4-ALIGNED_WIDTH{1'b0}},rw_addr_i[ALIGNED_WIDTH-1:0]};
	assign	addr_op2 = ({4{size_b}} & {4'b0000})
					| ({4{size_h}} & {4'b0001})
					| ({4{size_w}} & {4'b0011})
					| ({4{size_d}} & {4'b0111});
	assign	addr_end = addr_op1 + addr_op2;
	wire	overstep;				//是否超出一个对齐地址
	assign	overstep = (addr_end[3:ALIGNED_WIDTH] == 'd0) ? 1'b0 : 1'b1;
	wire	[2:0]	axi_size ;
	wire	[7:0]	axi_len;
	assign	axi_size = AXI_SIZE[2:0];
	assign	axi_len = aligned ? TRANS_LEN-1 : {7'd0,overstep};

	wire	[AXI_ADDR_WIDTH-1:0] axi_addr;			//对齐的传输地址
	assign	axi_addr = {rw_addr_i[AXI_ADDR_WIDTH-1:ALIGNED_WIDTH],{ALIGNED_WIDTH{1'b0}}};
	
//标志有效位
	wire	[OFFSET_WIDTH-1:0] aligned_offset_l;				//相对于对齐地址的偏移
	wire	[OFFSET_WIDTH-1:0] aligned_offset_h;
	assign	aligned_offset_l = {{OFFSET_WIDTH-ALIGNED_WIDTH{1'b0}},rw_addr_i[ALIGNED_WIDTH-1:0]} << 3;			//地址增量*8
	assign	aligned_offset_h = AXI_DATA_WIDTH - aligned_offset_l;
	wire	[MASK_WIDTH-1:0]	mask;
	assign	mask = (({MASK_WIDTH{size_b}} & {{MASK_WIDTH-8{1'B0}},8'hff})
				| ({MASK_WIDTH{size_h}} & {{MASK_WIDTH-16{1'b0}}, 16'hffff})
                | ({MASK_WIDTH{size_w}} & {{MASK_WIDTH-32{1'b0}}, 32'hffffffff})
                | ({MASK_WIDTH{size_d}} & {{MASK_WIDTH-64{1'b0}}, 64'hffffffff_ffffffff})
                ) << aligned_offset_l;
	wire	[AXI_DATA_WIDTH-1:0]	mask_l;
	wire	[AXI_DATA_WIDTH-1:0]	mask_h;
	assign	mask_h = mask[MASK_WIDTH-1:AXI_DATA_WIDTH];
	assign	mask_l = mask[AXI_DATA_WIDTH-1:0];
	
	wire [AXI_ID_WIDTH-1:0] axi_id = {AXI_ID_WIDTH{1'b0}};
    wire [AXI_USER_WIDTH-1:0] axi_user = {AXI_USER_WIDTH{1'b0}};
	
	  
//number of transaction
	
	reg	[7:0]	len;
	wire	len_reset = reset | (w_trans & (w_state == W_STATE_IDLE)) | (r_trans & (r_state == R_STATE_IDLE));
	wire	len_incr_en = (len != axi_len) & (w_hs | r_hs);
	always @(posedge clock)
	  begin
		if(len_reset)
			len <= 8'h00;
		else if(len_incr_en)
			len <= len + 1'b1;	  
	  end
	  
//write address channel signal
	assign	aw_valid_o = (w_state == W_STATE_ADDR);
	assign	aw_addr_o = axi_addr;
	assign	aw_id_o = axi_id;
	assign	aw_user_o = axi_user;
	assign	aw_prot_o = `AXI_PROT_UNPRIVILEGED_ACCESS | `AXI_PROT_SECURE_ACCESS | `AXI_PROT_DATA_ACCESS;		//????
	assign	aw_len_o = axi_len;
	assign	aw_size_o = axi_size;
	assign	aw_burst_o = `AXI_BURST_TYPE_INCR;
	assign	aw_lock_o = 1'b0;
	assign	aw_cache_o = `AXI_ARCACHE_NORMAL_NON_CACHEABLE_NON_BUFFERABLE;
	assign	aw_qos_o = 4'h0;

//write data channel
	assign	w_valid_o = (w_state == W_STATE_WRITE);
	wire	[AXI_DATA_WIDTH-1:0]	axi_w_data_l;
	wire	[AXI_DATA_WIDTH-1:0]	axi_w_data_h;
	assign	w_last_o = w_hs & (len == axi_len);
	assign	axi_w_data_l = data_write_i << aligned_offset_l;
	assign	axi_w_data_h = data_write_i >> aligned_offset_h;
	
genvar	j;
generate
	for(j = 0;j < TRANS_LEN;j = j + 1)	begin
		always @(negedge clock) begin
			if(reset)	begin
				w_data_o <= {AXI_DATA_WIDTH{1'b0}};
				w_strb_o <= 8'h00;
			end
			else if(w_ready_i & w_valid_o)	begin
				if(~aligned & overstep)	begin
					w_data_o <= (len[0] == 1'b0) ? axi_w_data_l : axi_w_data_h;
					w_strb_o <= (len[0] == 1'b0) ? {mask_l[56],mask_l[48],mask_l[40],mask_l[32],mask_l[24],mask_l[16],mask_l[8],mask_l[0]}
												: {mask_h[56],mask_h[48],mask_h[40],mask_h[32],mask_h[24],mask_h[16],mask_h[8],mask_h[0]};
				end
				else if(len == j)	begin
					w_data_o[AXI_DATA_WIDTH-1:0] <= axi_w_data_l[j*AXI_DATA_WIDTH+:AXI_DATA_WIDTH];
					w_strb_o <= {mask_l[56],mask_l[48],mask_l[40],mask_l[32],mask_l[24],mask_l[16],mask_l[8],mask_l[0]};
				
				end
			end
		end
	end
endgenerate

//write respomse channel
	assign	b_ready_o = (w_state ==W_STATE_RESP);




//read address channel
	assign	ar_valid_o = (r_state == R_STATE_ADDR);
	assign	ar_addr_o = axi_addr;
	assign	ar_prot_o = `AXI_PROT_UNPRIVILEGED_ACCESS | `AXI_PROT_SECURE_ACCESS | `AXI_PROT_DATA_ACCESS;
	assign	ar_id_o = axi_id;
	assign	ar_user_o = axi_user;
	assign	ar_len_o = axi_len;
	assign	ar_size_o = axi_size;
	assign	ar_burst_o = `AXI_BURST_TYPE_INCR;
	assign	ar_lock_o = 1'b0;
	assign	ar_cache_o = `AXI_ARCACHE_NORMAL_NON_CACHEABLE_NON_BUFFERABLE;

//read data channel
	assign	r_ready_o = (r_state == R_STATE_READ);
	wire	[AXI_DATA_WIDTH-1:0]	axi_r_data_l;
	wire	[AXI_DATA_WIDTH-1:0]	axi_r_data_h;
	assign	axi_r_data_l  = (r_data_i & mask_l) >> aligned_offset_l;
	assign	axi_r_data_h  = (r_data_i & mask_h) << aligned_offset_h;
	
genvar i;
generate
	for(i = 0;i < TRANS_LEN;i = i + 1)	begin
		always @(posedge clock)	begin
			if(reset)	begin
				data_read_o[i*AXI_DATA_WIDTH+:AXI_DATA_WIDTH] <= 0;
			end
			else if(r_ready_o & r_valid_i)	begin
				if(~aligned & overstep)	begin
					data_read_o[AXI_DATA_WIDTH-1:0] <= (len[0] == 1'b0) ? axi_r_data_l : 
																		(data_read_o[AXI_DATA_WIDTH-1:0] | axi_r_data_h);
				end
				else if(len == i)	begin
					data_read_o[i*AXI_DATA_WIDTH+:AXI_DATA_WIDTH] <= axi_r_data_l;
				
				end
			end
		end
	end

endgenerate


//rw_ready_o;rw_resp_o
	reg		rw_ready;
	assign	rw_ready_o = rw_ready;
always @(posedge clock)
  begin
	if(reset)	begin
		rw_ready <= 1'b0;
	end
	else if(trans_done | rw_ready)	begin
		rw_ready <= trans_done;
	end
  end
  
	reg		[1:0]	rw_resp;
	assign	rw_resp_o = rw_resp;
always @(posedge clock)
  begin
	if(reset)	begin
		rw_resp <= 2'b00;
	end
	else if(trans_done)	begin
		rw_resp <= w_trans ? b_resp_i : r_resp_i;
	end
  
  end

endmodule



















